﻿namespace ClassyBlog.ExtensionMethods
{
    using System;
    using System.Collections.Generic;
    using System.Linq;

    using System.Yaml;

    internal static class YamlExtensions
    {
        private static readonly Func<YamlScalar, string> trimValue
            = x => (x.Value ?? string.Empty).Trim();

        public static void Load<T>(
            this T instance,
            string content,
            IDictionary<string, Action<T, string, object>> knownSetters,
            Action<T, string, object> unknownSetter)
        {
            YamlNode[] nodes;

            try
            {
                nodes = YamlNode.FromYaml(content);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException(
                    TextMessages.YamlExtensions_Load_Malformed_Yaml, e);
            }

            if ((nodes == null) || (nodes.Count() != 1))
            {
                throw new InvalidOperationException(
                    TextMessages.YamlExtensions_Load_Invalid_Node);
            }

            var map = nodes.FirstOrDefault() as YamlMapping;

            if (map == null)
            {
                return;
            }

            foreach (var pair in map)
            {
                var key = pair.Key as YamlScalar;

                if ((key == null) || (key.Value == null))
                {
                    continue;
                }

                var property = key.Value.Trim();

                var setter = knownSetters.ContainsKey(property) ?
                             knownSetters[property] :
                             unknownSetter;

                var simple = pair.Value as YamlScalar;
                var list = pair.Value as YamlSequence;
                var hash = pair.Value as YamlMapping;

                object value;

                if (simple != null)
                {
                    value = Convert(simple);
                }
                else if (list != null)
                {
                    value = Convert(list);
                }
                else if (hash != null)
                {
                    value = Convert(hash);
                }
                else
                {
                    throw new InvalidOperationException(
                        TextMessages.
                        YamlExtensions_Load_Not_Known_Node.Interpolate(
                        pair.Value));
                }

                setter(instance, property, value);
            }
        }

        private static string Convert(YamlScalar node)
        {
            return (node.Value ?? string.Empty).Trim();
        }

        private static IEnumerable<string> Convert(IEnumerable<YamlNode> node)
        {
            return node.OfType<YamlScalar>()
                       .Select(x => x.Value)
                       .Where(x => !string.IsNullOrWhiteSpace(x))
                       .Select(x => x.Trim())
                       .ToList();
        }

        private static IDictionary<string, string> Convert(
            IEnumerable<KeyValuePair<YamlNode, YamlNode>> node)
        {
            return node.Select(x => x)
                       .Where(x => x.Key is YamlScalar &&
                           x.Value is YamlScalar)
                       .Select(x => new
                       {
                            Key = trimValue((YamlScalar)x.Key),
                            Value = trimValue((YamlScalar)x.Value)
                       })
                       .Where(x => !string.IsNullOrWhiteSpace(x.Key))
                       .ToDictionary(
                       x => x.Key,
                       x => x.Value,
                       StringComparer.OrdinalIgnoreCase);
        }
    }
}